home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / share / perl5 / PPI / Find.pm < prev    next >
Encoding:
Perl POD Document  |  2010-07-06  |  8.8 KB  |  401 lines

  1. package PPI::Find;
  2.  
  3. =pod
  4.  
  5. =head1 NAME
  6.  
  7. PPI::Find - Object version of the Element->find method
  8.  
  9. =head1 SYNOPSIS
  10.  
  11.   # Create the Find object
  12.   my $Find = PPI::Find->new( \&wanted );
  13.   
  14.   # Return all matching Elements as a list
  15.   my @found = $Find->in( $Document );
  16.   
  17.   # Can we find any matching Elements
  18.   if ( $Find->any_matches($Document) ) {
  19.       print "Found at least one matching Element";
  20.   }
  21.   
  22.   # Use the object as an iterator
  23.   $Find->start($Document) or die "Failed to execute search";
  24.   while ( my $token = $Find->match ) {
  25.       ...
  26.   }
  27.  
  28. =head1 DESCRIPTION
  29.  
  30. PPI::Find is the primary PDOM searching class in the core PPI package.
  31.  
  32. =head2 History
  33.  
  34. It became quite obvious during the development of PPI that many of the
  35. modules that would be built on top of it were going to need large numbers
  36. of saved, storable or easily creatable search objects that could be
  37. reused a number of times.
  38.  
  39. Although the internal ->find method provides a basic ability to search,
  40. it is by no means thorough. PPI::Find attempts to resolve this problem.
  41.  
  42. =head2 Structure and Style
  43.  
  44. PPI::Find provides a similar API to the popular L<File::Find::Rule>
  45. module for file searching, but without the ability to assemble queries.
  46.  
  47. The implementation of a separate PPI::Find::Rule sub-class that does
  48. provide this ability is left as an exercise for the reader.
  49.  
  50. =head2 The &wanted function
  51.  
  52. At the core of each PPI::Find object is a "wanted" function that is
  53. passed a number of arguments and returns a value which controls the
  54. flow of the search.
  55.  
  56. As the search executes, each Element will be passed to the wanted function
  57. in depth-first order.
  58.  
  59. It will be provided with two arguments. The current Element to test as $_[0],
  60. and the top-level Element of the search as $_[1].
  61.  
  62. The &wanted function is expected to return 1 (positive) if the Element
  63. matches the condition, 0 (false) if it does not, and undef (undefined) if
  64. the condition does not match, and the Find search should not descend to
  65. any of the current Element's children.
  66.  
  67. Errors should be reported from the &wanted function via die, which will be
  68. caught by the Find object and returned as an error.
  69.  
  70. =head1 METHODS
  71.  
  72. =cut
  73.  
  74. use strict;
  75. use Params::Util qw{_INSTANCE};
  76.  
  77. use vars qw{$VERSION};
  78. BEGIN {
  79.     $VERSION = '1.213';
  80. }
  81.  
  82.  
  83.  
  84.  
  85.  
  86. #####################################################################
  87. # Constructor
  88.  
  89. =pod
  90.  
  91. =head2 new &wanted
  92.  
  93. The C<new> constructor takes a single argument of the &wanted function,
  94. as described above and creates a new search.
  95.  
  96. Returns a new PPI::Find object, or C<undef> if not passed a CODE reference.
  97.  
  98. =cut
  99.  
  100. sub new {
  101.     my $class  = ref $_[0] ? ref shift : shift;
  102.     my $wanted = ref $_[0] eq 'CODE' ? shift : return undef;
  103.  
  104.     # Create the object
  105.     my $self = bless {
  106.         wanted => $wanted,
  107.     }, $class;
  108.  
  109.     $self;
  110. }
  111.  
  112. =pod
  113.  
  114. =head2 clone
  115.  
  116. The C<clone> method creates another instance of the same Find object.
  117.  
  118. The cloning is done safely, so if your existing Find object is in the
  119. middle of an iteration, the cloned Find object will not also be in the
  120. iteration and can be safely used independently.
  121.  
  122. Returns a duplicate PPI::Find object.
  123.  
  124. =cut
  125.  
  126. sub clone {
  127.     my $self = ref $_[0] ? shift
  128.         : die "->clone can only be called as an object method";
  129.     my $class = ref $self;
  130.  
  131.     # Create the object
  132.     my $clone = bless {
  133.         wanted => $self->{wanted},
  134.     }, $class;
  135.  
  136.     $clone;
  137. }
  138.  
  139.  
  140.  
  141.  
  142.  
  143. ####################################################################
  144. # Search Execution Methods
  145.  
  146. =pod
  147.  
  148. =head2 in $Document [, array_ref => 1 ]
  149.  
  150. The C<in> method starts and completes a full run of the search.
  151.  
  152. It takes as argument a single L<PPI::Element> object which will
  153. serve as the top of the search process.
  154.  
  155. Returns a list of PPI::Element objects that match the condition
  156. described by the &wanted function, or the null list on error.
  157.  
  158. You should check the ->errstr method for any errors if you are
  159. returned the null list, which may also mean simply that no Elements
  160. were found that matched the condition.
  161.  
  162. Because of this need to explicitly check for errors, an alternative
  163. return value mechanism is provide. If you pass the C<array_ref => 1>
  164. parameter to the method, it will return the list of matched Elements
  165. as a reference to an ARRAY. The method will return false if no elements
  166. were matched, or C<undef> on error.
  167.  
  168. The ->errstr method can still be used to get the error message as normal.
  169.  
  170. =cut
  171.  
  172. sub in {
  173.     my $self    = shift;
  174.     my $Element = shift;
  175.     my %params  = @_;
  176.     delete $self->{errstr};
  177.  
  178.     # Are we already acting as an iterator
  179.     if ( $self->{in} ) {
  180.         return $self->_error('->in called while another search is in progress', %params);
  181.     }
  182.  
  183.     # Get the root element for the search
  184.     unless ( _INSTANCE($Element, 'PPI::Element') ) {
  185.         return $self->_error('->in was not passed a PPI::Element object', %params);
  186.     }
  187.  
  188.     # Prepare the search
  189.     $self->{in}      = $Element;
  190.     $self->{matches} = [];
  191.  
  192.     # Execute the search
  193.     eval {
  194.         $self->_execute;
  195.     };
  196.     if ( $@ ) {
  197.         my $errstr = $@;
  198.         $errstr =~ s/\s+at\s+line\s+.+$//;
  199.         return $self->_error("Error while searching: $errstr", %params);
  200.     }
  201.  
  202.     # Clean up and return
  203.     delete $self->{in};
  204.     if ( $params{array_ref} ) {
  205.         if ( @{$self->{matches}} ) {
  206.             return delete $self->{matches};
  207.         }
  208.         delete $self->{matches};
  209.         return '';
  210.     }
  211.  
  212.     # Return as a list
  213.     my $matches = delete $self->{matches};
  214.     @$matches;
  215. }
  216.  
  217. =pod
  218.  
  219. =head2 start $Element
  220.  
  221. The C<start> method lets the Find object act as an iterator. The method
  222. is passed the parent PPI::Element object as for the C<in> method, but does
  223. not accept any parameters.
  224.  
  225. To simplify error handling, the entire search is done at once, with the
  226. results cached and provided as-requested.
  227.  
  228. Returns true if the search completes, and false on error.
  229.  
  230. =cut
  231.  
  232. sub start {
  233.     my $self    = shift;
  234.     my $Element = shift;
  235.     delete $self->{errstr};
  236.  
  237.     # Are we already acting as an iterator
  238.     if ( $self->{in} ) {
  239.         return $self->_error('->in called while another search is in progress');
  240.     }
  241.  
  242.     # Get the root element for the search
  243.     unless ( _INSTANCE($Element, 'PPI::Element') ) {
  244.         return $self->_error('->in was not passed a PPI::Element object');
  245.     }
  246.  
  247.     # Prepare the search
  248.     $self->{in}      = $Element;
  249.     $self->{matches} = [];
  250.  
  251.     # Execute the search
  252.     eval {
  253.         $self->_execute;
  254.     };
  255.     if ( $@ ) {
  256.         my $errstr = $@;
  257.         $errstr =~ s/\s+at\s+line\s+.+$//;
  258.         $self->_error("Error while searching: $errstr");
  259.         return undef;
  260.     }
  261.  
  262.     1;
  263. }
  264.  
  265. =pod
  266.  
  267. =head2 match
  268.  
  269. The C<match> method returns the next matching Element in the iteration.
  270.  
  271. Returns a PPI::Element object, or C<undef> if there are no remaining
  272. Elements to be returned.
  273.  
  274. =cut
  275.  
  276. sub match {
  277.     my $self = shift;
  278.     return undef unless $self->{matches};
  279.  
  280.     # Fetch and return the next match
  281.     my $match = shift @{$self->{matches}};
  282.     return $match if $match;
  283.  
  284.     $self->finish;
  285.     undef;
  286. }
  287.  
  288. =pod
  289.  
  290. =head2 finish
  291.  
  292. The C<finish> method provides a mechanism to end iteration if you wish to
  293. stop the iteration prematurely. It resets the Find object and allows it to
  294. be safely reused.
  295.  
  296. A Find object will be automatically finished when C<match> returns false.
  297. This means you should only need to call C<finnish> when you stop
  298. iterating early.
  299.  
  300. You may safely call this method even when not iterating and it will return
  301. without failure.
  302.  
  303. Always returns true
  304.  
  305. =cut
  306.  
  307. sub finish {
  308.     my $self = shift;
  309.     delete $self->{in};
  310.     delete $self->{matches};
  311.     delete $self->{errstr};
  312.     1;
  313. }
  314.  
  315.  
  316.  
  317.  
  318.  
  319. #####################################################################
  320. # Support Methods and Error Handling
  321.  
  322. sub _execute {
  323.     my $self   = shift;
  324.     my $wanted = $self->{wanted};
  325.     my @queue  = ( $self->{in} );
  326.  
  327.     # Pull entries off the queue and hand them off to the wanted function
  328.     while ( my $Element = shift @queue ) {
  329.         my $rv = &$wanted( $Element, $self->{in} );
  330.  
  331.         # Add to the matches if returns true
  332.         push @{$self->{matches}}, $Element if $rv;
  333.  
  334.         # Continue and don't descend if it returned undef
  335.         # or if it doesn't have children
  336.         next unless defined $rv;
  337.         next unless $Element->isa('PPI::Node');
  338.  
  339.         # Add the children to the head of the queue
  340.         if ( $Element->isa('PPI::Structure') ) {
  341.             unshift @queue, $Element->finish if $Element->finish;
  342.             unshift @queue, $Element->children;
  343.             unshift @queue, $Element->start if $Element->start;
  344.         } else {
  345.             unshift @queue, $Element->children;
  346.         }
  347.     }
  348.  
  349.     1;
  350. }
  351.  
  352. =pod
  353.  
  354. =head2 errstr
  355.  
  356. The C<errstr> method returns the error messages when a given PPI::Find
  357. object fails any action.
  358.  
  359. Returns a string, or C<undef> if there is no error.
  360.  
  361. =cut
  362.  
  363. sub errstr {
  364.     shift->{errstr};
  365. }
  366.  
  367. sub _error {
  368.     my $self = shift;
  369.     $self->{errstr} = shift;
  370.     my %params = @_;
  371.     $params{array_ref} ? undef : ();
  372. }
  373.  
  374. 1;
  375.  
  376. =pod
  377.  
  378. =head1 TO DO
  379.  
  380. - Implement the L<PPI::Find::Rule> class
  381.  
  382. =head1 SUPPORT
  383.  
  384. See the L<support section|PPI/SUPPORT> in the main module.
  385.  
  386. =head1 AUTHOR
  387.  
  388. Adam Kennedy E<lt>adamk@cpan.orgE<gt>
  389.  
  390. =head1 COPYRIGHT
  391.  
  392. Copyright 2001 - 2010 Adam Kennedy.
  393.  
  394. This program is free software; you can redistribute
  395. it and/or modify it under the same terms as Perl itself.
  396.  
  397. The full text of the license can be found in the
  398. LICENSE file included with this module.
  399.  
  400. =cut
  401.